package com.iwhalecloud.citybrain.flow.platform.manager.application.event;


import cn.hutool.core.util.URLUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.iwhalecloud.citybrain.flow.platform.manager.application.utils.ExpressionCmd;
import com.iwhalecloud.citybrain.flow.platform.manager.application.utils.SpringUtils;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.exception.EventCallException;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.exception.EventCallSuspendException;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.gateway.http.EventCallGateway;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.FlowModelVariableVo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.eventconfig.DeployedEventConfigDomain;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.eventconfig.EventCallInterface;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.task.TaskVariableSetRequestVo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.repository.TaskVariableRepository;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.flowable.engine.ManagementService;
import org.flowable.engine.common.api.variable.VariableContainer;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.event.impl.FlowableActivityEventImpl;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Future;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
@Component
public class EventCallURL implements ApplicationContextAware, EventCallInterface {
    @Autowired
    static EventCallGateway eventCallGateway;

    @Autowired
    private NodeEventExecuter nodeEventExecuter;

    @Lazy
    @Autowired
    private TaskVariableRepository taskVariableRepository;

    /**
     * 当前任务节点实体
     */
    private TaskEntity taskEntity;

    /**
     * 活动节点实体
     */
    private FlowableActivityEventImpl activityEvent;

    /**
     * 解析后的URL
     */
    private String parsedUrl;

    /**
     * 异步：true；同步：false
     */
    private boolean async;

    /**
     * 解析后的用户填入的post body
     */
    private String requestBody;

    /**
     * 数据实体对象，用于解析变量
     */
    private VariableContainer variableContainer;


    /**
     * eventConfig
     */
    private DeployedEventConfigDomain eventConfig;

    private EventCallURL() {
    }

    public EventCallURL(DelegateExecution execution) {
        this.variableContainer = execution;
    }

    public EventCallURL(FlowableActivityEventImpl activityEvent, DeployedEventConfigDomain config) {
        this.async = "async".equals(config.getCallMethod());
        this.eventConfig = config;
        this.activityEvent = activityEvent;
        this.variableContainer = activityEvent.getExecution();
    }

    public EventCallURL(TaskEntity taskEntity, DeployedEventConfigDomain config) {
        this.async = "async".equals(config.getCallMethod());
        this.eventConfig = config;
        this.taskEntity = taskEntity;
        this.variableContainer = taskEntity;
    }

    public void doProcess() throws EventCallSuspendException {
        // 解析url
        String url = eventConfig.getCallbackContent();
        if (url.isEmpty()) {
            log.info("无事件URL");
            return;
        }
        log.info("解析前的请求地址：{}", url);
        if (Objects.nonNull(taskEntity)) {
            variableContainer.setTransientVariable("taskId", taskEntity.getId());
            variableContainer.setTransientVariable("assignee", taskEntity.getAssignee());
            variableContainer.setTransientVariable("nodeCode", taskEntity.getTaskDefinitionKey());
            variableContainer.setTransientVariable("nodeName", taskEntity.getName());
        }
        parsedUrl = parseStr(url);
        if (parsedUrl == null) {
            log.error("流程变量解析失败: {}", url);
            throw new EventCallSuspendException("同步请求流程变量解析失败，报错中止! url: " + url);
        }
        log.info("解析后的请求地址：{}", parsedUrl);

        requestBody = parseStr(eventConfig.getRequestBody());

        log.info("解析后的请求体：{}", requestBody);

        if (Objects.isNull(nodeEventExecuter)) {
            nodeEventExecuter = SpringUtils.getBean(NodeEventExecuter.class);
        }
        if (async) {
            try {
                nodeEventExecuter.asyncCall(parsedUrl, requestBody);
            } catch (EventCallException e) {
                log.error("execute Async EventCall Error:", e);
            }
        } else {
            try {
                EventResponse eventResponse = nodeEventExecuter.syncCall(parsedUrl, requestBody);
                if (Objects.isNull(eventResponse)) {
                    throw new EventCallSuspendException("事件接口返回结果为空，无法判断接口调用是否成功，url=" + parsedUrl);
                }
                if (!eventResponse.isSuccess()) {
                    StringBuilder msg = new StringBuilder("事件接口调用异常，异常信息为：");
                    if (StringUtils.isNotBlank(eventResponse.getMsg())) {
                        msg.append(eventResponse.getMsg());
                    } else if (StringUtils.isNotBlank(eventResponse.getErrMsg())) {
                        msg.append(eventResponse.getErrMsg());
                    }
                    msg.append(",url=").append(parsedUrl);
                    throw new EventCallSuspendException(msg.toString());
                }
            } catch (EventCallException e) {
                log.error("execute Sync EventCall Error:", e);
                throw new EventCallSuspendException(e.getMessage());
            }
        }
    }

    public void doProcess(String eventType) throws EventCallSuspendException {
        // 解析url
        String url = eventConfig.getCallbackContent();
        if (url.isEmpty()) {
            log.info("无事件URL");
            return;
        }
        log.info("解析前的请求地址：{}", url);
        String processInstanceId = "";
        if (Objects.nonNull(taskEntity)) {
            variableContainer.setTransientVariable("taskId", taskEntity.getId());
            variableContainer.setTransientVariable("assignee", taskEntity.getAssignee());
            variableContainer.setTransientVariable("nodeCode", taskEntity.getTaskDefinitionKey());
            variableContainer.setTransientVariable("nodeName", taskEntity.getName());
            processInstanceId = taskEntity.getProcessInstanceId();
        }
        //结束节点的taskentity是空的 尝试从variableContainer中获取processInstanceId
        if (StringUtils.isBlank(processInstanceId) && Objects.nonNull(variableContainer)) {
            String temp = JSONObject.toJSONString(variableContainer);
            JSONObject jsonObject = JSON.parseObject(temp).getJSONObject("parent");
            processInstanceId = jsonObject.getString("processInstanceId");
        }
        parsedUrl = parseStr(url);
        if (parsedUrl == null) {
            log.error("流程变量解析失败: {}", url);
            throw new EventCallSuspendException("同步请求流程变量解析失败，报错中止! url: " + url);
        }
        log.info("解析后的请求地址：{}", parsedUrl);

        requestBody = parseStr(eventConfig.getRequestBody());

        log.info("解析后的请求体：{}", requestBody);

        if (Objects.isNull(nodeEventExecuter)) {
            nodeEventExecuter = SpringUtils.getBean(NodeEventExecuter.class);
        }
        EventResponse eventResponse = null;
        if (async) {
            try {
                Future<EventResponse> future = nodeEventExecuter.asyncCall(parsedUrl, requestBody, processInstanceId, eventConfig.getNodeCode(), eventConfig.getModelId(), eventConfig.getModelKey(), eventConfig.getModelName(), eventConfig.getName(), eventConfig.getDeploymentId(), eventConfig.getCallbackType(), eventType);
                eventResponse = future.get();
            } catch (Exception e) {
                log.error("execute Async EventCall Error:", e);
            }
        } else {
            try {
                eventResponse = nodeEventExecuter.syncCall(parsedUrl, requestBody, processInstanceId, eventConfig.getNodeCode(), eventConfig.getModelId(), eventConfig.getModelKey(), eventConfig.getModelName(), eventConfig.getName(), eventConfig.getDeploymentId(), eventConfig.getCallbackType(), eventType);
                if (Objects.isNull(eventResponse)) {
                    throw new EventCallSuspendException(eventType + "事件接口返回结果为空，无法判断接口调用是否成功，url=" + parsedUrl);
                }
                if (!eventResponse.isSuccess()) {
                    StringBuilder msg = new StringBuilder(eventType + "事件接口调用异常，异常信息为：");
                    if (StringUtils.isNotBlank(eventResponse.getMsg())) {
                        msg.append(eventResponse.getMsg());
                    }
                    msg.append(",url=").append(parsedUrl);
                    throw new EventCallSuspendException(msg.toString());
                }
            } catch (EventCallException e) {
                log.error("execute Sync EventCall Error:", e);
                throw new EventCallSuspendException(e.getMessage());
            }
        }
        if (StringUtils.isNotEmpty(eventConfig.getCallbackParam())){
            if(eventResponse != null && eventResponse.isSuccess() && eventResponse.getDatas() != null){
                Object responseData = eventResponse.getDatas();
                TaskVariableSetRequestVo taskVariableSetRequestVo = new TaskVariableSetRequestVo();
                taskVariableSetRequestVo.setKey(eventConfig.getCallbackParam());
                taskVariableSetRequestVo.setValue(responseData);
                taskVariableSetRequestVo.setProcessInstanceId(processInstanceId);
//                if(Objects.nonNull(taskEntity)){
//                    taskVariableSetRequestVo.setWorkItemId(taskEntity.getId());
//                }
                try {
                    taskVariableRepository.setTaskVariable(taskVariableSetRequestVo);
                } catch (Exception e) {
                    e.printStackTrace();
                    log.error("execute EventCall set param Error:",e);
                }
            }
        }
    }

    /**
     * 解析并替换url的参数,需要解析的参数使用${}包裹，直接根据${}解析。
     * 查找顺序：局部流程变量 -> 全局流程变量 -> 找不到报错
     *
     * @param originalStr 原字符
     * @return 解析后的url
     */
    public String parseStr(String originalStr) {
        String pattern = "\\$\\{\\w+(\\.\\w+)*\\}";
        // 创建 Pattern 对象
        Pattern r = Pattern.compile(pattern);
        // 现在创建 matcher 对象
        Matcher m = r.matcher(originalStr);
        ManagementService managementService = SpringUtils.getBean(ManagementService.class);

        while (m.find()) {
            String findResult = m.group(0).trim();
            Object value = null;
            try {
                ExpressionCmd expressionCmd = new ExpressionCmd(findResult, variableContainer);
                value = managementService.executeCommand(expressionCmd);
            } catch (Exception e) {
                log.error(String.format("解析表达式异常，表达式= %s，变量= %s", findResult, JSONObject.toJSONString(variableContainer)));
            }

            String matchVariable = null;
            if (Objects.nonNull(value)) {
                matchVariable = String.valueOf(value);
            }
            // 去除${}
            findResult = StringUtils.stripStart(findResult, "${");
            findResult = StringUtils.stripEnd(findResult, "}");
            // 局部流程变量 -> 全局流程变量 -> 找不到报错
            if (StringUtils.isNotBlank(matchVariable)) {
                // 替换所有变量
                originalStr = originalStr.replaceAll("\\$\\{" + findResult + "}", URLUtil.encode(matchVariable, StandardCharsets.UTF_8));
            }
        }
        return originalStr;
    }


    /**
     * 保存流程变量
     */
    public void saveModelVariable(List<Map<String, String>> response) {
        if (response != null) {
            String processDefinitionId = activityEvent != null ? activityEvent.getProcessDefinitionId() : taskEntity.getProcessDefinitionId();
            String executionId = activityEvent != null ? activityEvent.getExecutionId() : taskEntity.getExecutionId();
            eventCallGateway.saveActModelVariable(executionId, response);
        }
    }


    /**
     * 过滤并保存模型变量 <br/>
     * 对于"url请求返回的每个变量"，筛选出与数据库中"手动添加的变量"匹配的变量，保存到全局流程变量 <br/>
     * 对于剩余不匹配的，以value=null保存到 全局流程变量
     *
     * @param processDefinitionId 流程定义id
     * @param response            URL请求后返回的内容
     */
    public void filterAndSaveModelVariable(String processDefinitionId, List<Map<String, String>> response) {
        if (response == null) {
            return;
        }
        String deploymentId = eventCallGateway.resolveDeploymentId(processDefinitionId);
        List<FlowModelVariableVo> flowModelVariables = eventCallGateway.getFlowModelVariables(deploymentId);
        List<Map<String, String>> matchVariableList = new ArrayList<>();
        List<Map<String, String>> emptyVariableList = new ArrayList<>();
        // 匹配的保存流程变量
        response.stream().filter(
                (resp) -> flowModelVariables.stream().anyMatch(
                        (item) -> item.getName().equals(resp.get("name"))
                                && item.getCode().equals(resp.get("code"))
                )
        ).forEach(matchVariableList::add);
        log.info("更新匹配的流程变量：{}", matchVariableList);
        saveModelVariable(matchVariableList);
        // 剩余不匹配的，以value=null保存到 全局流程变量
        response.removeAll(matchVariableList);
        response.forEach((item) -> {
            Map<String, String> map = new HashMap<>();
            map.put("name", item.get("name"));
            map.put("code", item.get("code"));
            map.put("value", null);
            emptyVariableList.add(map);
        });
        log.info("更新不匹配的流程变量(value=null)：{}", emptyVariableList);
        saveModelVariable(emptyVariableList);
    }


    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        eventCallGateway = applicationContext.getBean(EventCallGateway.class);
        taskVariableRepository = applicationContext.getBean(TaskVariableRepository.class);
    }
}